import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

//import Sender.Handshake.ParallelHS;

public class Handshaker {
		
		//ATTRIBUTI DELLA CLASSE
		DatagramSocket sendingSocket;
		DatagramSocket ACKsocket;
		InetAddress IPDestination;
		int portDestination;
		InetAddress IPChannel;
		int portChannel;
		InetAddress IPSource;
		int portSource;
		int portACK;
		double x;
		int H;
		long fileLength;
		
		int N;
		double y;
		int L;
		int Lr;
		
		//METODO COSTRUTTORE
		public Handshaker (InetAddress IPDestination, int portDestination, InetAddress IPChannel, int portChannel, InetAddress IPSource, int portSource, int portACK, double x, int H, long fileLength, DatagramSocket sendingSocket, DatagramSocket ACKsocket){
			this.IPDestination = IPDestination;
			this.portDestination = portDestination;
			this.IPChannel = IPChannel;
			this.portChannel = portChannel;
			this.IPSource = IPSource;
			this.portSource = portSource;
			this.portACK = portACK;
			this.x = x;
			this.H = H;
			this.fileLength = fileLength;
			this.sendingSocket = sendingSocket;
			this.ACKsocket = ACKsocket;
		}
		
		//METODI DELLA CLASSE
		@SuppressWarnings("deprecation")
		public void start() throws IOException, InterruptedException{
			
			int Lf = (int)Math.round(this.dicotomic(10, x, x, H));
			byte[] message = new byte[Lf-H];
			
			message[0] = (byte)Math.round(x*1000);  
			Packet syn = new Packet(this.IPDestination, this.portDestination, this.IPSource, this.portACK, 65530, message);
			DatagramPacket UDPpacketS = new DatagramPacket(syn.toByte(), syn.toByte().length, IPChannel, portChannel);
			
			byte[] UDPdata = new byte[10000];
			DatagramPacket UDPpacketR = new DatagramPacket(UDPdata, UDPdata.length);
			
			ParallelSending ps = new ParallelSending(this.sendingSocket, UDPpacketS);
			Thread t = new Thread(ps);
			t.start();
			Packet syn1 = null;
			while (!ps.getResponse()){
				this.ACKsocket.receive(UDPpacketR);
				syn1 = new Packet(UDPpacketR.getData());
				if (syn1.getIDPacket() == 65531){
					ps.setResponse();
					System.out.println("Ricevuto: syn1");
				}
			}
			this.y = (double)syn1.getData()[0]/1000; 
			this.L = (int)Math.round(this.dicotomic(0.4, x, y, H));
			this.N = (int)Math.ceil((double)this.fileLength/(this.L - H));
			
			message = new byte[L - H];
			
			message[1] = (byte)(N / 256 - 128);
			message[2] = (byte)(N % 256 -128);
			
			Packet syn2 = new Packet(this.IPDestination, this.portDestination, this.IPSource, this.portACK, 65532, message);
			UDPpacketS = new DatagramPacket(syn2.toByte(), syn2.toByte().length, this.IPChannel, this.portChannel);
			
			UDPdata = new byte[10000];
			UDPpacketR = new DatagramPacket(UDPdata, UDPdata.length);
			
			ps = new ParallelSending(this.sendingSocket, UDPpacketS);
			t = new Thread(ps);
			t.start();
			Packet syn3;
			while (!ps.getResponse()){
				this.ACKsocket.receive(UDPpacketR);
				syn3 = new Packet(UDPpacketR.getData());
				if (syn3.getIDPacket() == 65533){
					ps.setResponse();
					System.out.println("Ricevuto: syn3");
			}
	
		}
		
			
		}
		
		public int getN() {
			return this.N;
		}
		
		public int getL() {
			return L;
		}
		
		public double gety() {
			return y;
		}
		
		
		//METODI PRIVATI
		private double dicotomic (double tol, double x, double y, int H) {
			
			double inf = 100;
			double sup = 9000;
			boolean trovato = false;

			double d_inf = x*Math.exp(inf/1024)*(inf-(H+1024))*inf*Math.pow(Math.log(inf),2)-1048576*(inf*(1+Math.log(inf))-H)-y*1024*inf*Math.pow(Math.log(inf),2);
			double d_sup = x*Math.exp(sup/1024)*(sup-(H+1024))*sup*Math.pow(Math.log(sup),2)-1048576*(sup*(1+Math.log(sup))-H)-y*1024*sup*Math.pow(Math.log(sup),2);
			
			if (d_inf*d_sup > 0)
				return -1;//////////////////
			
			double m = (sup + inf)/2;
			double d_m = x*Math.exp(m/1024)*(m-(H+1024))*m*Math.pow(Math.log(m),2)-1048576*(m*(1+Math.log(m))-H)-y*1024*m*Math.pow(Math.log(m),2);
			if (d_m == 0)
				trovato = true;
			while (!trovato && ((sup - inf) > tol)) {
				if (d_inf*d_m > 0) {
					inf = m;
					d_inf = x*Math.exp(inf/1024)*(inf-(H+1024))*inf*Math.pow(Math.log(inf),2)-1048576*(inf*(1+Math.log(inf))-H)-y*1024*inf*Math.pow(Math.log(inf),2);
				} else {
					sup = m;
					d_sup = x*Math.exp(sup/1024)*(sup-(H+1024))*sup*Math.pow(Math.log(sup),2)-1048576*(sup*(1+Math.log(sup))-H)-y*1024*sup*Math.pow(Math.log(sup),2);
				}
				m = (sup + inf)/2;
				d_m = x*Math.exp(m/1024)*(m-(H+1024))*m*Math.pow(Math.log(m),2)-1048576*(m*(1+Math.log(m))-H)-y*1024*m*Math.pow(Math.log(m),2);
				if (d_m == 0)
					trovato = true;
			}
			return m;
		}
		
		//CLASSI INTERNE
		private class ParallelSending implements Runnable {
			
			//ATTRIBUTI DI CLASSE
			private DatagramPacket UDPpacket;
			private DatagramSocket socket;
			private boolean response;
			// METODO COSTRUTTORE
			public ParallelSending(DatagramSocket socket, DatagramPacket UDPpacket){
				this.socket = socket;
				this.UDPpacket = UDPpacket;
				this.response = false;
			}
			//METODI DI CLASSE
			public void run(){
				System.out.println("Invio..");
				while(!response)
					try {
						socket.send(UDPpacket);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
			}
			public void setResponse(){
				this.response = true;
			}
			public boolean getResponse(){
				return this.response;
			}
		}
}
